/*
 *
 * Copyright (c) 2020 The Raptor Authors. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

#include "raptor-lite/utils/slice_buffer.h"
#include <string.h>
#include <iterator>
#include "raptor-lite/utils/log.h"
#include "raptor-lite/utils/useful.h"

namespace raptor {

class SliceBufferImpl {
public:
    SliceBufferImpl()
        : length_(0) {}
    ~SliceBufferImpl() {}

    Slice Merge() const;
    size_t SliceCount() const { return ds_.size(); }
    size_t GetBufferLength() const { return length_; }
    void AddSlice(const Slice &s);
    void AddSlice(Slice &&s);

    Slice GetHeader(size_t len);
    bool MoveHeader(size_t len);
    void ClearBuffer() {
        ds_.clear();
        length_ = 0;
    }

    bool Empty() const { return ds_.empty(); }
    const Slice &Front() const { return ds_.front(); }
    const Slice &Back() const { return ds_.back(); }

    void PopFront();
    void PopBack();

    const Slice &operator[](size_t n) const { return ds_[n]; }
    inline const Slice &At(size_t n) const { return ds_.at(n); }

    size_t CopyToBuffer(void *buff, size_t len);

private:
    size_t length_;
    std::deque<Slice> ds_;
};

Slice SliceBufferImpl::Merge() const {

    if (SliceCount() == 0) {
        return Slice();
    }

    if (SliceCount() == 1) {
        return ds_[0];
    }

    size_t len = GetBufferLength();
    Slice ret = MakeSliceByLength(len);
    char *buf = ret.buffer();
    for (auto it = ds_.begin(); it != ds_.end(); ++it) {
        size_t blk_size = it->size();
        memcpy(buf, it->begin(), blk_size);
        buf += blk_size;
    }
    return ret;
}

void SliceBufferImpl::AddSlice(const Slice &s) {
    if (s.empty()) return;
    ds_.push_back(s);
    length_ += s.size();
}

void SliceBufferImpl::AddSlice(Slice &&s) {
    if (s.empty()) return;
    ds_.emplace_back(s);
    length_ += s.size();
}

Slice SliceBufferImpl::GetHeader(size_t len) {
    if (len == 0 || GetBufferLength() < len) {
        return Slice();
    }

    Slice s = MakeSliceByLength(len);
    CopyToBuffer(s.buffer(), len);
    return s;
}

bool SliceBufferImpl::MoveHeader(size_t len) {
    if (GetBufferLength() < len) {
        return false;
    }
    if (len == 0) {
        return true;
    }

    auto it = ds_.begin();
    size_t left = it->size();

    if (left > len) {
        it->RemoveHead(len);
        length_ -= len;
    } else if (left == len) {
        length_ -= len;
        ds_.erase(it);
    } else {
        // len > left
        length_ -= left;
        ds_.erase(it);

        return MoveHeader(len - left);
    }
    return true;
}

size_t SliceBufferImpl::CopyToBuffer(void *buffer, size_t length) {
    RAPTOR_ASSERT(length <= GetBufferLength());

    auto it = ds_.begin();

    size_t left = length;
    size_t pos = 0;

    while (it != ds_.end() && left != 0) {

        size_t len = RAPTOR_MIN(left, it->size());
        memcpy((uint8_t *)buffer + pos, it->begin(), len);

        left -= len;
        pos += len;

        ++it;
    }

    return pos;
}

void SliceBufferImpl::PopFront() {
    RAPTOR_ASSERT(!ds_.empty());
    length_ -= ds_.front().size();
    ds_.pop_front();
}

void SliceBufferImpl::PopBack() {
    RAPTOR_ASSERT(!ds_.empty());
    length_ -= ds_.back().size();
    ds_.pop_back();
}

// -------------------------------------------

SliceBuffer::SliceBuffer()
    : impl_(new SliceBufferImpl()) {}

SliceBuffer::~SliceBuffer() { delete impl_; }

Slice SliceBuffer::Merge() const { return impl_->Merge(); }

size_t SliceBuffer::SliceCount() const { return impl_->SliceCount(); }

size_t SliceBuffer::GetBufferLength() const { return impl_->GetBufferLength(); }

void SliceBuffer::AddSlice(const Slice &s) { return impl_->AddSlice(s); }

void SliceBuffer::AddSlice(Slice &&s) { return impl_->AddSlice(s); }

Slice SliceBuffer::GetHeader(size_t len) { return impl_->GetHeader(len); }

bool SliceBuffer::MoveHeader(size_t len) { return impl_->MoveHeader(len); }

size_t SliceBuffer::CopyToBuffer(void *ptr, size_t len) { return impl_->CopyToBuffer(ptr, len); }

void SliceBuffer::ClearBuffer() { impl_->ClearBuffer(); }

bool SliceBuffer::Empty() const { return impl_->Empty(); }

const Slice &SliceBuffer::Front() const { return impl_->Front(); }

const Slice &SliceBuffer::Back() const { return impl_->Back(); }

void SliceBuffer::PopFront() { impl_->PopFront(); }

void SliceBuffer::PopBack() { impl_->PopBack(); }

const Slice &SliceBuffer::At(size_t n) const { return impl_->At(n); }

const Slice &SliceBuffer::operator[](size_t n) const { return impl_->operator[](n); }

} // namespace raptor
